package qsoft.omanga.common.utils;

import android.graphics.Bitmap;
import android.os.Handler;
import android.util.Log;

import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 
 * @author sontt
 * 
 */
public class Cache {
	private static final Cache CACHE = new Cache();

	public static final Cache getInstance() {
		return CACHE;
	}

	/**
	 * Cache-related fields and methods.
	 * 
	 * We use a hard and a soft cache. A soft reference cache is too
	 * aggressively cleared by the Garbage Collector.
	 */
	private final int hardCacheCapacity = 10;
	/**
     * 
     */
	private final int delayBeforePurge = 10 * 1000; // in milliseconds

	/**
	 * @category Hard cache, with a fixed maximum capacity and a life duration
	 */
	private final Map<String, Bitmap> hardBitmapCache = Collections
			.synchronizedMap(new LinkedHashMap<String, Bitmap>(
					hardCacheCapacity / 2, 0.75f, true) {
				/**
                 * 
                 */
				private static final long serialVersionUID = -236931563639218743L;

				@Override
				protected boolean removeEldestEntry(
						LinkedHashMap.Entry<String, Bitmap> eldest) {
					if (size() > hardCacheCapacity) {
						// Entries push-out of hard reference cache are
						// transferred to
						// soft reference cache
						softBitmapCache.put(eldest.getKey(),
								new SoftReference<Bitmap>(eldest.getValue()));
						return true;
					} else {
						return false;
					}
				}
			});

	/**
	 * @category Soft cache for bitmaps kicked out of hard cache
	 */
	private final Map<String, SoftReference<Bitmap>> softBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(
			hardCacheCapacity / 2);

	/**
	 * @category Direct soft cache for bitmap
	 */
	private final Map<String, SoftReference<Bitmap>> directSoftCacheBitmap = new ConcurrentHashMap<String, SoftReference<Bitmap>>();
	/**
     * 
     */
	private final Handler purgeHandler = new Handler();

	/**
     * 
     */
	private final Runnable purger = new Runnable() {
		public void run() {
			clearCache();
		}
	};

	/**
	 * Adds this bitmap to the cache.
	 * 
	 * @param url
	 *            The URL of the image that will be retrieved from the cache.
	 * @param bitmap
	 *            The newly loaded bitmap.
	 */
	public void addBitmapToCache(String url, Bitmap bitmap) {
		if (bitmap != null) {
			hardBitmapCache.put(url, bitmap);
		}
	}

	/**
	 * @param url
	 *            The URL of the image that will be retrieved from the cache.
	 * @return The cached bitmap or null if it was not found.
	 */
	public Bitmap getBitmapFromCache(String url) {
		// First try the hard reference cache
		final Bitmap bitmap = hardBitmapCache.get(url);
		if (bitmap != null) {
			// Bitmap found in hard cache
			// Move element to first position, so that it is removed last
			hardBitmapCache.remove(url);
			hardBitmapCache.put(url, bitmap);
			Log.e("CACHE -- CACHE -- CACHE -- " + url, "HARD CACHE " + url);
			return bitmap;
		}

		// Then try the soft reference cache
		SoftReference<Bitmap> bitmapReference = softBitmapCache.get(url);
		if (bitmapReference != null) {
			final Bitmap bmp = bitmapReference.get();
			if (bmp != null) {
				// Bitmap found in soft cache
				Log.e("CACHE -- CACHE -- CACHE -- " + url, "SOFT CACHE " + url);
				return bmp;
			} else {
				// Soft reference has been Garbage Collected
				softBitmapCache.remove(url);
			}
		}
		return null;
	}

	/**
	 * Clears the image cache used internally to improve performance. Note that
	 * for memory efficiency reasons, the cache will automatically be cleared
	 * after a certain inactivity delay.
	 */
	private void clearCache() {
		Log.e("CLEAR CACHE", "CLEAR CACHE -- CLEAR CACHE -- CLEAR CACHE");
		hardBitmapCache.clear();
		softBitmapCache.clear();
	}

	/**
	 * Allow a new delay before the automatic cache clear is done.
	 */
	public void resetPurgeTimer() {
		purgeHandler.removeCallbacks(purger);
		purgeHandler.postDelayed(purger, delayBeforePurge);
	}

	/**
	 * 
	 * @param url
	 *            url of image
	 * @return Bitmap
	 */
	public Bitmap getSoftCacheImage(String url) {
		if (!directSoftCacheBitmap.containsKey(url)) {
			return null;
		}
		SoftReference<Bitmap> ref = directSoftCacheBitmap.get(url);
		return ref.get();
	}

	/**
	 * 
	 * @param url
	 *            url of image
	 * @param bitmap
	 */
	public void putSoftCacheImage(String url, Bitmap bitmap) {
		directSoftCacheBitmap.put(url, new SoftReference<Bitmap>(bitmap));
	}
}
